namespace Sorter
{
    public partial class GemSorter : Form
    {
        // option
        Dictionary<int, Resolution> resList = new Dictionary<int, Resolution>();
        Resolution currentRes = new Resolution();
        //----------

        // Bring to front
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        private IntPtr handle;
        private Process process;
        //------------------

        // input
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;
        //-----------------

        string stashInfo = "";

        List<Gem> gemList = new List<Gem>();

        Gem[,] gemArray = new Gem[12, 12];

        public GemSorter()
        {
            InitializeComponent();

            FillResolutionList();

            OptionFile();

            GetPOEWindow();

            // Get string of gems from web
            WebSetUp();
        }

        private void OptionFile()
        {
            string file = @"C:\gemsort\gemsort.txt";
            StreamWriter writer;
            StreamReader reader;
            bool found = false;

            if (File.Exists(file))
            {
                found = true;
            }
            else
            {
                Directory.CreateDirectory(@"C:\gemsort\");
                writer = new StreamWriter(File.Create(file));
                writer.WriteLine("0");
                found = true;
                writer.Close();
            }

            if (found)
            {
                reader = new StreamReader(file);
                string s = reader.ReadLine();

                int id = int.Parse(s);

                currentRes = resList[id];

                reader.Close();
            }
        }

        private void FillResolutionList()
        {
            // 1920*1080
            Resolution r = new Resolution();
            r.id = 0;
            r.dx = 53;
            r.dy = 53;
            r.offX = 40;
            r.offY = 188;
            resList.Add(r.id, r);

            // 1280*1024
            r = new Resolution();
            r.id = 1;
            r.dx = 47;
            r.dy = 47;
            r.offX = 38;
            r.offY = 165;
            resList.Add(r.id, r);

            // 1680*1050
            r = new Resolution();
            r.id = 2;
            r.dx = 49;
            r.dy = 49;
            r.offX = 37;
            r.offY = 186;
            resList.Add(r.id, r);

            // 1600*900
            r = new Resolution();
            r.id = 3;
            r.dx = 49;
            r.dy = 49;
            r.offX = 37;
            r.offY = 186;
            resList.Add(r.id, r);
        }

        private void GetPOEWindow()
        {
            // Find path of exile window(handle) use this later to move to foreground
            Process[] processlist = Process.GetProcesses();

            foreach (Process proc in processlist)
            {
                if (!string.IsNullOrEmpty(proc.MainWindowTitle))
                {
                    if (proc.MainWindowTitle.Contains("Path of Exile"))
                    {
                        process = proc;
                        handle = process.MainWindowHandle;
                    }
                }
            }
        }

        private void WebSetUp()
        {
            // start the browser at path of exile login page
            webBrowser.Navigate("https://www.pathofexile.com/login");
        }

        private void getStash_Click(object sender, EventArgs e)
        {
            listBox.Items.Clear();
            gemList.Clear();
            stashInfo = "";

            for (int x = 0; x < 12; x++)
            {
                for (int y = 0; y < 12; y++)
                {
                    Gem ng = new Gem();
                    gemArray[x, y] = ng;
                }
            }

            // Get the webpage element source and strip out the text i need.
            HtmlDocument doc = webBrowser.Document;
            HtmlElementCollection col = doc.GetElementsByTagName("div");
            foreach (HtmlElement element in col)
            {
                string cls = element.GetAttribute("className");

                if (cls.Contains("iconContainer"))
                {
                    HtmlElement el = element;
                    HtmlElement parent = el.Parent;

                    string inner = el.InnerHtml.ToString();
                    string style = parent.Style.ToString();

                    stashInfo += inner + " " + style + " #";
                }
            }

            // DEBUG
            StreamWriter writer = new StreamWriter(File.Create(@"C:/gemsort/log.txt"));
            writer.Write(stashInfo);
            writer.Close();
            //stashInfo = "";
            //StreamReader reader = new StreamReader(@"C:\Users\sebbn\Desktop\stash.txt");
            //stashInfo = reader.ReadToEnd();
            //reader.Close();
            //--------------------
            

            ParseStashInfo();

            sortAndFillArray();

            repopulateGemList();
        }

        private void ParseStashInfo()
        {     
            string temp = stashInfo;
            string[] arr = stashInfo.Split('#');

            // find position and gemname from where the different gem pictures are placed on the webpage
            int i = 0;
            foreach (string s in arr)
            {
                if (!s.Contains("Gem"))
                    continue;

                int start = s.IndexOf("Gems/");
                string tmp = s.Remove(0, start + 5);

                string name = tmp.Split('?')[0];
                name = name.Replace(".png", "");
                if (name.Contains("Support"))
                    name = name.Replace("Support/", "X_");

                int leftIndex = tmp.IndexOf("left", StringComparison.OrdinalIgnoreCase);
                int topIndex = tmp.IndexOf("top", StringComparison.OrdinalIgnoreCase);

                string left = tmp.Remove(0, leftIndex);
                left = left.Remove(left.IndexOf("px"));

                string top = tmp.Remove(0, topIndex);
                top = top.Remove(top.IndexOf("px"));

                left = left.Replace("left:", "");
                left = left.Replace("LEFT:", "");
                left = left.Replace("Left:", "");
                left = left.Replace("px", "");
                left = left.Replace("PX", "");
                left = left.Replace(".", ",");

                top = top.Replace("top:", "");
                top = top.Replace("TOP:", "");
                top = top.Replace("Top:", "");
                top = top.Replace("px", "");
                top = top.Replace("PX", "");
                top = top.Replace(".", ",");

                Gem gem = new Gem();
                gem.ID = i;
                gem.Name = name;
                gem.X = (int)float.Parse(left);
                gem.Y = (int)float.Parse(top);

                if (gem.newX != 0 || gem.newY != 0)
                    continue;

                gemList.Add(gem);

                i++;
            }
        }

        private void sortAndFillArray()
        {
            foreach (Gem g in gemList)
            {
                // 47 is the size of a gem image at web stash but delta x delta y is 48 at uneven x and y using i%2 to check
                Gem ng = g.Copy();
                int x = 0, y = 0;
                int dx = 14 - 47;
                int dy = 143 - 47;

                for (int i = 0; i < 12; i++)
                {
                    dx += 47;
                    if (i % 2 != 0)
                        dx += 1;

                    if (Math.Abs(ng.X - dx) < 5)
                    {
                        x = i;
                        break;
                    }
                }

                for (int i = 0; i < 12; i++)
                {
                    dy += 47;
                    if (i % 2 != 0)
                        dy += 1;

                    if (Math.Abs(ng.Y - dy) < 5)
                    {
                        y = i;
                        break;
                    }
                }

                gemArray[x, y] = ng;
            }
        }

        private void repopulateGemList()
        {
            gemList.Clear();

            for (int x = 0; x < 12; x++)
            {
                for (int y = 0; y < 12; y++)
                {
                    Gem g = new Gem();
                    g.Name = "Zero";
                    g.ID = -1;
                    if (gemArray[x, y].X == 0)
                        gemList.Add(g);
                    else
                    {
                        Gem ng = gemArray[x, y].Copy();
                        ng.X = x;
                        ng.Y = y;
                        gemList.Add(ng);
                    }
                }
            }

            gemList.Sort();

            int n = 0;
            int k = 0;
            for (int i = 0; i < gemList.Count; i++)
            {
                Gem g = gemList[i].Copy();
                g.newX = k;
                g.newY = n;
                gemList[i] = g;

                n++;
                if (n == 12)
                {
                    n = 0;
                    k++;
                }
            }

            foreach (Gem g in gemList)
            {
                if (g.ID >= 0)
                    gemArray[(int)g.X, (int)g.Y] = g;

                if (g.Name != "Zero") 
                    listBox.Items.Add(g.Name);
            }
        }

        // start the in-game sorting
        private void Go()
        {
            // after "sort" button have been clicked, wait 5 seconds then start sorting.
            System.Threading.Thread.Sleep(5000);
            // Create a copy of array to keep track of init pos
            Gem[,] temp = new Gem[12, 12];
            for (int x = 0; x < 12; x++)
            {
                for (int y = 0; y < 12; y++)
                {
                    Gem g = new Gem();
                    g = gemArray[x, y].Copy();
                    temp[x, y] = g;
                }
            }

            // pick up initial gem and place empty slot
            Gem emptyGem = new Gem();
            Gem currentGem = temp[0, 0];
            Point p = GetXY(0, 0);
            GemClick(p);
            temp[0, 0] = emptyGem;

            int i = 0;
            while (i < 12 * 12)
            {
                // Check if gem have been moved
                if (!currentGem.moved)
                {
                    // current gem is not at correct place already
                    if (currentGem.X != currentGem.newX || currentGem.Y != currentGem.newY)
                    {
                        int x = (int)currentGem.X;
                        int y = (int)currentGem.Y;
                        int newX = currentGem.newX;
                        int newY = currentGem.newY;

                        // place gem at new pos, and ofc pick up the gem at that pos
                        p = GetXY(newX, newY);
                        GemClick(p);

                        // flip gem place in matrix and place new gem in currentGem
                        Gem tGem = currentGem;
                        tGem.moved = true;
                        currentGem = temp[newX, newY];
                        temp[newX, newY] = tGem;
                    }
                    else // current gem is at correct place, pick a new gem
                    {
                        temp[(int)currentGem.X, (int)currentGem.Y].moved = true;
                        bool found = false;
                        for (int x = 0; x < 12; x++)
                        {
                            for (int y = 0; y < 12; y++)
                            {
                                if (!temp[x, y].moved)
                                {
                                    currentGem = temp[x, y];
                                    found = true;
                                    break;
                                }
                            }
                            if (found)
                                break;
                        }
                    }
                }
                else // current gem have been moved, pick a new one
                {
                    bool found = false;
                    for (int x = 0; x < 12; x++)
                    {
                        for (int y = 0; y < 12; y++)
                        {
                            if (!temp[x, y].moved)
                            {
                                currentGem = temp[x, y];
                                found = true;
                                break;
                            }
                        }
                        if (found)
                            break;
                    }
                }

                // if gem is empty get new gem
                if (string.IsNullOrEmpty(currentGem.Name))
                {
                    bool found = false;
                    for (int x = 0; x < 12; x++)
                    {
                        for (int y = 0; y < 12; y++)
                        {
                            if (!temp[x, y].moved)
                            {
                                currentGem = temp[x, y];
                                found = true;
                                break;
                            }
                        }
                        if (found)
                            break;
                    }
                }

                i++;
            }

        }

        // Move cursor to position of a gem and click it
        private void GemClick(Point p)
        {
            int x = p.X;
            int y = p.Y;

            this.Cursor = new Cursor(Cursor.Current.Handle);
            Cursor.Position = new Point(x, y);

            System.Threading.Thread.Sleep(200);  // incase of lag          
            int X = Cursor.Position.X;
            int Y = Cursor.Position.Y;
            mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);

            System.Threading.Thread.Sleep(40);
            mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);

            System.Threading.Thread.Sleep(40);
        }

        private Point GetXY(float X, float Y)
        {
            int x = (int)X;
            int y = (int)Y;
            int startx = currentRes.offX;
            int starty = currentRes.offY;

            return new Point(
                startx + x * currentRes.dx, 
                starty + y * currentRes.dy);
        }

        private bool isGemMoved(Gem g)
        {
            return true;
        }

        private void GoBtn_Click_1(object sender, EventArgs e)
        {
            SetForegroundWindow(handle);
            Go();
        }

        // 1920*1080
        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            currentRes = resList[0];
            string path = @"C:\gemsort\gemsort.txt";
            File.WriteAllText(path, String.Empty);
            StreamWriter writer = new StreamWriter(path);
            writer.WriteLine("0");
            writer.Close();
        }

        // 1280*1024
        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            currentRes = resList[1];
            string path = @"C:\gemsort\gemsort.txt";
            File.WriteAllText(path, String.Empty);
            StreamWriter writer = new StreamWriter(path);
            writer.WriteLine("1");
            writer.Close();
        }

        // 1680*1050
        private void toolStripMenuItem4_Click(object sender, EventArgs e)
        {
            currentRes = resList[2];
            string path = @"C:\gemsort\gemsort.txt";
            File.WriteAllText(path, String.Empty);
            StreamWriter writer = new StreamWriter(path);
            writer.WriteLine("2");
            writer.Close();
        }

        // 1600*900
        private void toolStripMenuItem5_Click(object sender, EventArgs e)
        {
            currentRes = resList[3];
            string path = @"C:\gemsort\gemsort.txt";
            File.WriteAllText(path, String.Empty);
            StreamWriter writer = new StreamWriter(path);
            writer.WriteLine("3");
            writer.Close();
        }
    }

    public struct Resolution
    {
        public int id;
        public int offX;
        public int offY;
        public int dx;
        public int dy;
    }

    public struct Gem : IComparable<Gem>
    {
        public int ID;
        public int newX;
        public int newY;
        public int X;
        public int Y;
        public string Name;
        public bool moved;

        public Gem Copy()
        {
            Gem g = new Gem();
            g.ID = this.ID;
            g.X = this.X;
            g.Y = this.Y;
            g.newX = this.newX;
            g.newY = this.newY;
            g.Name = this.Name;
            g.moved = false;
            return g;
        }

        public int CompareTo(Gem b)
        {
            return this.Name.CompareTo(b.Name);
        }
    }

    public class sortOnX : IComparer<Gem>
    {
        public int Compare(Gem a, Gem b)
        {
            if (a.X > b.X) return 1;
            else if (a.X < b.X) return -1;
            else return 0;
        }
    }

    public class sortOnY : IComparer<Gem>
    {
        public int Compare(Gem a, Gem b)
        {
            if (a.Y > b.Y) return 1;
            else if (a.Y < b.Y) return -1;
            else return 0;
        }
    }
}
